/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.spider.web.service.impl;

import lombok.extern.slf4j.Slf4j;
import me.zhengjie.config.FileProperties;
import me.zhengjie.exception.SpiderRunningException;
import me.zhengjie.spider.comic.domain.SpiderComic;
import me.zhengjie.spider.comic.service.SpiderComicService;
import me.zhengjie.spider.common.SpiderAttributeBean;
import me.zhengjie.spider.common.SpiderConstant;
import me.zhengjie.spider.manager.SpiderAsyncManager;
import me.zhengjie.spider.manager.SpiderComicManager;
import me.zhengjie.spider.manager.SpiderVideoManager;
import me.zhengjie.spider.rule.domain.SpiderRule;
import me.zhengjie.spider.rule.repository.SpiderRuleRepository;
import me.zhengjie.spider.util.SpiderUtil;
import me.zhengjie.spider.video.domain.SpiderVideo;
import me.zhengjie.spider.video.repository.SpiderVideoRepository;
import me.zhengjie.spider.web.domain.SpiderWeb;
import me.zhengjie.utils.*;
import lombok.RequiredArgsConstructor;
import me.zhengjie.spider.web.repository.SpiderWebRepository;
import me.zhengjie.spider.web.service.SpiderWebService;
import me.zhengjie.spider.web.service.dto.SpiderWebDto;
import me.zhengjie.spider.web.service.dto.SpiderWebQueryCriteria;
import me.zhengjie.spider.web.service.mapstruct.SpiderWebMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;
import java.util.Map;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.LinkedHashMap;

/**
 * @author 雨来
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2023-07-07
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class SpiderWebServiceImpl implements SpiderWebService {

    private final SpiderWebRepository spiderWebRepository;
    private final SpiderWebMapper spiderWebMapper;
    private final SpiderVideoRepository spiderVideoRepository;
    private final SpiderRuleRepository spiderRuleRepository;
    private final SpiderVideoManager spiderVideoManager;
    private final SpiderComicManager spiderComicManager;
    private final SpiderAsyncManager spiderAsyncManager;
    private final FileProperties fileProperties;
    private final RedisUtils redisUtils;
    private final SpiderComicService spiderComicService;

    @Override
    public PageResult<SpiderWebDto> queryAll(SpiderWebQueryCriteria criteria, Pageable pageable) {
        Page<SpiderWeb> page = spiderWebRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(spiderWebMapper::toDto));
    }

    @Override
    public List<SpiderWebDto> queryAll(SpiderWebQueryCriteria criteria) {
        return spiderWebMapper.toDto(spiderWebRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public SpiderWebDto findById(Long webId) {
        SpiderWeb spiderWeb = spiderWebRepository.findById(webId).orElseGet(SpiderWeb::new);
        ValidationUtil.isNull(spiderWeb.getWebId(), "SpiderWeb", "webId", webId);
        return spiderWebMapper.toDto(spiderWeb);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(SpiderWeb resources) {
        resources.setDeletedFlag(false);
        spiderWebRepository.save(resources);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(SpiderWeb resources) {
        SpiderWeb spiderWeb = spiderWebRepository.findById(resources.getWebId()).orElseGet(SpiderWeb::new);
        ValidationUtil.isNull(spiderWeb.getWebId(), "SpiderWeb", "id", resources.getWebId());
        spiderWeb.copy(resources);
        spiderWebRepository.save(spiderWeb);
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long webId : ids) {
            spiderWebRepository.deleteById(webId);
        }
    }

    @Override
    public void download(List<SpiderWebDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (SpiderWebDto spiderWeb : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("域名", spiderWeb.getDomain());
            map.put("网站地址", spiderWeb.getWebAddress());
            map.put("备用地址", spiderWeb.getBackupAddress());
            map.put("网站名称", spiderWeb.getWebName());
            map.put("网站类型", spiderWeb.getWebType());
            map.put("删除状态", spiderWeb.getDeletedFlag());
            map.put("备注", spiderWeb.getRemark());
            map.put("更新时间", spiderWeb.getUpdateTime());
            map.put("创建时间", spiderWeb.getCreateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public ResponseEntity<Object> spiderAction(SpiderWeb resources) {
        // 爬虫配置
        SpiderAttributeBean spiderAttributeBean = SpiderAttributeBean.builder().thread(20).build();
        try {
            spiderVideoManager.doSpider(resources, spiderAttributeBean);
        } catch (Exception e) {
            throw new SpiderRunningException("[videoManager.doSpider] 爬取视频基本信息失败！");
        }
        return ResponseEntity.ok(null);
    }
    @Override
    public ResponseEntity<Object> spiderComicAction(SpiderWeb resources) {
        // 爬虫配置
        SpiderAttributeBean spiderAttributeBean = SpiderAttributeBean.builder().thread(20).build();
        try {
            spiderComicManager.doSpider(resources, spiderAttributeBean);
        } catch (Exception e) {
            throw new SpiderRunningException("[videoManager.doSpider] 爬取漫画基本信息失败！");
        }
        return ResponseEntity.ok(null);
    }

    @Override
    public ResponseEntity<Object> spiderComicDetailAction(SpiderWeb resources) {
        // 爬虫配置
        SpiderAttributeBean spiderAttributeBean = SpiderAttributeBean.builder().thread(1).build();
        try {
            spiderComicManager.afterDoSpider(resources, spiderAttributeBean);
        } catch (Exception e) {
            throw new SpiderRunningException("[videoManager.spiderComicDetailAction] 爬取视频详细信息失败！");
        }
        return ResponseEntity.ok(null);
    }


    @Override
    public ResponseEntity<Object> spiderComicContentAction(SpiderWeb resources) {
        // 爬虫配置
        SpiderAttributeBean spiderAttributeBean = SpiderAttributeBean.builder().thread(1).build();
        try {
            spiderComicManager.spiderComicContent(resources, spiderAttributeBean);
        } catch (Exception e) {
            throw new SpiderRunningException("[videoManager.spiderComicDetailAction] 爬取视频详细信息失败！");
        }
        return ResponseEntity.ok(null);
    }

    @Override
    public ResponseEntity<Object> resetComicRedisCache(Long webId) {
        spiderComicManager.resetRedisCache(webId);
        return ResponseEntity.ok(null);
    }

    @Override
    public ResponseEntity<Object> cacheComicImgByWebId(Long webId) {
        List<SpiderComic> spiderComicList = spiderComicService.findByWebId(webId);
        for (SpiderComic spiderComic : spiderComicList) {
            if (StringUtils.isNotBlank(spiderComic.getImg()) && StringUtils.isNotBlank(spiderComic.getLocalPath())) {
                spiderAsyncManager.downloadImg(spiderComic.getImg(), spiderComic.getLocalPath());
            }

        }
        return ResponseEntity.ok(null);
    }

    @Override
    public ResponseEntity<Object> spiderDetailAction(SpiderWeb resources) {
        // 爬虫配置
        SpiderAttributeBean spiderAttributeBean = SpiderAttributeBean.builder().thread(1).build();
        try {
            spiderVideoManager.afterDoSpider(resources, spiderAttributeBean);
        } catch (Exception e) {
            throw new SpiderRunningException("[videoManager.doSpider] 爬取视频详细信息失败！");
        }
        return ResponseEntity.ok(null);
    }

    @Override
    public ResponseEntity<Object> resetRedisCache(Long webId) {
        log.info("重置网站ID为{}的缓存", webId);
        long startTime = System.currentTimeMillis();
        spiderVideoManager.resetRedisCache(webId);
        long endTime = System.currentTimeMillis();
        log.info("重置网站ID为{}的缓存耗时：{}秒", webId, (endTime - startTime) / 1000);
        return ResponseEntity.ok(null);
    }

    @Override
    public ResponseEntity<Object> cacheImgByWebId(Long webId) {
        List<SpiderVideo> spiderVideoList = spiderVideoRepository.findByWebId(webId);
        for (SpiderVideo spiderVideo : spiderVideoList) {
            if (StringUtils.isNotBlank(spiderVideo.getImg()) && StringUtils.isNotBlank(spiderVideo.getLocalPath())) {
                spiderAsyncManager.downloadImg(spiderVideo.getImg(), spiderVideo.getLocalPath());
            }

        }
        return ResponseEntity.ok(null);
    }

    @Override
    public ResponseEntity<Object> cacheM3U8ByWebId(Long webId) {
        List<SpiderVideo> spiderVideoList = spiderVideoRepository.findByWebId(webId);
        SpiderRule tsNameRule = spiderRuleRepository.findByWebIdAndFieldCode(webId, SpiderConstant.TS_NAME);
        for (SpiderVideo spiderVideo : spiderVideoList) {
            if (StringUtils.endsWith(spiderVideo.getPlayLink(), SpiderConstant.FILE_TYPE_M3U8) && StringUtils.isNotBlank(spiderVideo.getPlayLink()) && StringUtils.isNotBlank(spiderVideo.getLocalPath())) {
                spiderAsyncManager.downloadM3U8(spiderVideo.getPlayLink(), spiderVideo.getLocalPath(), tsNameRule.getRule());
            }
        }
        return ResponseEntity.ok(null);
    }

    @Override
    public ResponseEntity<Object> overwriteM3u8File(Long webId) {
        List<SpiderVideo> spiderVideoList = spiderVideoRepository.findByWebId(webId);
        SpiderRule byWebIdAndFieldCode = spiderRuleRepository.findByWebIdAndFieldCode(webId, SpiderConstant.TS_NAME);
        String baseUrl = fileProperties.getPath().getPath();
        String preDir = baseUrl.endsWith(SpiderConstant.FOLDER_DIR) ? baseUrl : baseUrl + SpiderConstant.FOLDER_DIR;
        for (SpiderVideo spiderVideo : spiderVideoList) {
            String localPath = preDir + spiderVideo.getLocalPath() + SpiderConstant.FILE_NAME_M3U8;
            if (StringUtils.isNotBlank(spiderVideo.getPlayLink()) && StringUtils.isNotBlank(spiderVideo.getLocalPath())) {
                String redisKey = String.format(SpiderConstant.SPIDER_VIDEO_KEY, spiderVideo.getWebId().toString());
                String item = String.format("%s%s%s", webId, SpiderConstant.COLON, spiderVideo.getLocalPath());
                if (!this.redisUtils.hHasKey(redisKey, item)) {
                    this.redisUtils.hset(redisKey, item, null);
                    SpiderUtil.overwriteM3u8File(spiderVideo.getPlayLink(), localPath, byWebIdAndFieldCode.getRule());
                    log.info("[{}]更新成功", spiderVideo.getAlt());
                }
            }
        }
        return ResponseEntity.ok(null);
    }
}